#pragma once

#include <vector>
#include <utility>
#include <climits>
#include <set>
#include <sstream>
#include <functional>
#include <string>
#include <thread>
#include "Util.hpp"
#include "Log.hpp"
#include "online.hpp"
#include "../rpc_common/Util.hpp"
#include "../rpc_client/Rpc_client.hpp"

#define BOARD_ROW 15
#define BOARD_COL 15
#define CHESS_WHITE 1
#define CHESS_BLACK 2

typedef enum
{
    GAME_START = 0,
    GAME_OVER
} room_statu;
class room
{
public:
    room(uint64_t room_id, online_manager *online_user, Rpc::Client::RpcClient::ptr clientp) : _room_id(room_id), _statu(GAME_START), _player_count(0),
                                                                                               _online_user(online_user),
                                                                                               _white_id(UINT64_MAX), _black_id(UINT16_MAX),
                                                                                               _is_ai_room(false), _clientp(clientp),
                                                                                               _board(std::invoke([&]
                                                                                                                  { return std::vector(static_cast<int>(BOARD_ROW),
                                                                                                                                       std::vector(BOARD_COL, 0)); }))
    {
        ILOG("room:%lu init success!", _room_id);
    }
    ~room()
    {
        ILOG("room:%lu destroy", _room_id);
    }
    uint64_t id()
    {
        return _room_id;
    }
    room_statu statu()
    {
        return _statu;
    }
    int player_count()
    {
        return _player_count;
    }
    void add_white_user(uint64_t uid)
    {
        _white_id = uid;
        _player_count++;
    }
    void add_black_user(uint64_t uid)
    {
        _black_id = uid;
        _player_count++;
    }
    uint64_t get_white_user()
    {
        return _white_id;
    }
    uint64_t get_black_user()
    {
        return _black_id;
    }
    uint64_t get_white_id()
    {
        return _white_id;
    }

    // 处理下棋动作
    Json::Value handle_chess(Json::Value &req)
    {
        Json::Value json_resp = req;
        // 先判断房间中两个玩家是否都在线，任意一个不在线，就是另一方胜利。
        if (_online_user->is_in_game_room(_white_id) == false)
        {
            json_resp["result"] = true;
            json_resp["reason"] = "对方离开房间，你赢了！";
            json_resp["winner"] = (Json::UInt64)_black_id;
            return json_resp;
        }
        if (_online_user->is_in_game_room(_black_id) == false)
        {
            json_resp["result"] = true;
            json_resp["reason"] = "对方离开房间，你赢了！";
            json_resp["winner"] = (Json::UInt64)_white_id;
            return json_resp;
        }
        int chess_row = req["row"].asInt();
        int chess_col = req["col"].asInt();
        uint64_t cur_uid = req["uid"].asUInt64();
        int cur_color = cur_uid == _white_id ? CHESS_WHITE : CHESS_BLACK;
        // 判断当前走棋是否合理（位置是否已经被占用）
        if (_board[chess_row][chess_col] != 0)
        {
            json_resp["result"] = false;
            json_resp["reason"] = "当前位置已有其它棋子！";
            return json_resp;
        }
        _board[chess_row][chess_col] = cur_color;

        // 判断是否有玩家胜利
        Json::Value request;
        request["row"] = chess_row;
        request["col"] = chess_col;
        request["color"] = cur_color;
        uint64_t winner_id = 0;
        if (!is_win_wuzi(request, winner_id))
        {
            json_resp["result"] = false;
            json_resp["reason"] = "rpc调用失败!";
            return json_resp;
        }
        json_resp["winner"] = 0;
        if (winner_id > 0)
        {
            if (winner_id == _black_id)
            {
                json_resp["reason"] = "黑方胜利！";
                json_resp["winner"] = _black_id;
            }
            else
            {
                json_resp["reason"] = "白方胜利！";
                json_resp["winner"] = _white_id;
            }
        }
        json_resp["result"] = true;
        return json_resp;
    }
    // 处理聊天动作
    Json::Value handle_chat(Json::Value &req)
    {
        Json::Value json_resp = req;
        // todo:可进行敏感词判断
        json_resp["result"] = true;
        return json_resp;
    }
    // 处理玩家退出房间动作
    void handle_exit(uint64_t uid)
    {
        // 如果是下棋中退出(掉线)，则对方胜利，否则是正常退出
        Json::Value json_resp;
        if (_statu == GAME_START)
        {
            uint64_t winner_id = (Json::UInt64)(uid == _white_id ? _black_id : _white_id);
            json_resp["optype"] = "put_chess";
            json_resp["result"] = true;
            json_resp["reason"] = "对方掉线，你赢了！";
            json_resp["room_id"] = (Json::UInt64)_room_id;
            json_resp["uid"] = (Json::UInt64)uid;
            json_resp["row"] = -1;
            json_resp["col"] = -1;
            json_resp["winner"] = (Json::UInt64)winner_id;
            uint64_t loser_id = winner_id == _white_id ? _black_id : _white_id;
            if (winner_id != UINT64_MAX)
            {
                Json::Value params, result;
                params["uid"] = winner_id;
                if (!_clientp->call("win", params, result))
                {
                    json_resp["result"] = false;
                    json_resp["reason"] = "rpc调用失败!";
                    ELOG("handle_chess: call win failed!");
                }
                if (!result["result"].asBool())
                {
                    ELOG("handle_chess: win err, %s", result["reason"].asCString());
                }
            }
            if (loser_id != UINT64_MAX)
            {
                Json::Value params, result;
                params["uid"] = loser_id;
                if (!_clientp->call("lose", params, result))
                {
                    json_resp["result"] = false;
                    json_resp["reason"] = "rpc调用失败!";
                    ELOG("handle_chess: call lose failed!");
                }
                if (!result["result"].asBool())
                {
                    ELOG("handle_chess: lose err, %s", result["reason"].asCString());
                }
            }
            _statu = GAME_OVER;
            broadcast(json_resp);
        }
        // 房间中玩家数量--
        _player_count--;
        return;
    }
    void broadcast(Json::Value rsp)
    {
        // 序列化rsp
        std::string rsp_str;
        if (!json_util::serialize(rsp, rsp_str))
        {
            ELOG("room: broadcast serialize rsp failed!");
        }
        // 获取房间中所有用户的通信连接
        wsserver_t::connection_ptr wconn;
        wsserver_t::connection_ptr bconn;
        if (_white_id != UINT64_MAX)
        {
            wconn = _online_user->get_conn_from_room(_white_id);
        }
        if (_black_id != UINT64_MAX)
        {
            bconn = _online_user->get_conn_from_room(_black_id);
        }
        // 发送响应
        if (wconn.get() != nullptr)
        {
            wconn->send(rsp_str);
        }
        if (bconn.get() != nullptr)
        {
            bconn->send(rsp_str);
        }
        return;
    }
    // 处理ai对战下棋动作(玩家)
    Json::Value ai_handle_chess_user(Json::Value &req)
    {
        Json::Value json_resp = req;
        int chess_row = req["row"].asInt();
        int chess_col = req["col"].asInt();
        uint64_t cur_uid = req["uid"].asUInt64();
        // 判断当前走棋是否合理（位置是否已经被占用）
        if (_board[chess_row][chess_col] != 0)
        {
            json_resp["result"] = false;
            json_resp["reason"] = "当前位置已有其他棋子！";
            return json_resp;
        }
        int cur_color = CHESS_BLACK;              // ai对战默认黑方为玩家
        _board[chess_row][chess_col] = cur_color; // 落子
        Json::Value request;
        request["row"] = chess_row;
        request["col"] = chess_col;
        request["color"] = cur_color;
        uint64_t winner_id = 0;
        if (!is_win_wuzi(request, winner_id))
        {
            json_resp["result"] = false;
            json_resp["reason"] = "rpc调用失败!";
            return json_resp;
        }
        if (winner_id > 0)
        {
            json_resp["reason"] = "黑方胜利！";
            json_resp["winner"] = _black_id;
        }
        json_resp["result"] = true;
        return json_resp;
    }
    // 处理ai对战下棋动作(ai)
    Json::Value ai_handle_chess_user_ai()
    {
        Json::Value json_resp;
        Json::Value params, result, board_info;
        for (int i = 0; i < BOARD_ROW; i++)
        {
            for (int j = 0; j < BOARD_COL; j++)
            {
                std::stringstream ss;
                if (_board[i][j] == 0)
                {
                    continue;
                }
                else if (_board[i][j] == 1)
                {
                    ss << i << ',' << j;
                    board_info["white"].append(ss.str());
                }
                else
                {
                    ss << i << ',' << j;
                    board_info["black"].append(ss.str());
                }
            }
        }
        params["board_info"] = board_info;
        params["color"] = CHESS_WHITE;
        params["white_id"] = _white_id;
        params["black_id"] = _black_id;
        if (!_clientp->call("ai_wuzi", params, result))
        {
            json_resp["result"] = false;
            json_resp["reason"] = "rpc调用失败!";
            ELOG("ai_handle_chess_user: call ai_wuzi failed!");
            return json_resp;
        }
        if (!result["result"].asBool())
        {
            std::string err_str = result["reason"].asString();
            ELOG("ai_handle_chess_user_ai: ai_wuzi err: %s", err_str.c_str());
            err_str += " rpc error!";
            json_resp["result"] = false;
            json_resp["reason"] = err_str;
            return json_resp;
        }
        int row = result["pos"][0].asInt();
        int col = result["pos"][1].asInt();
        _board[row][col] = CHESS_WHITE; // 落子
        // 判断是否有玩家胜利
        Json::Value request;
        request["row"] = row;
        request["col"] = col;
        request["color"] = CHESS_WHITE;
        uint64_t winner_id = 0;
        if (!is_win_wuzi(request, winner_id))
        {
            json_resp["result"] = false;
            json_resp["reason"] = "rpc调用失败!";
            return json_resp;
        }
        json_resp["winner"] = 0;
        if (winner_id > 0)
        {
            json_resp["reason"] = "白方胜利！";
            json_resp["winner"] = _white_id;
        }
        json_resp["result"] = true;
        json_resp["optype"] = "put_chess";
        json_resp["row"] = row;
        json_resp["col"] = col;
        return json_resp;
    }

    // 总的room请求处理函数，根据不同的请求类型调用不同的处理函数，将结果广播
    void handle_request(Json::Value &req)
    {
        // 校验房间号是否匹配
        Json::Value json_resp;
        uint64_t room_id = req["room_id"].asUInt64();
        if (room_id != _room_id)
        {
            json_resp["optype"] = req["optype"].asString();
            json_resp["result"] = false;
            json_resp["reason"] = "房间号不匹配！";
            return broadcast(json_resp);
        }
        // 根据不同的请求类型调用不同的处理函数
        if (req["optype"].asString() == "put_chess")
        {
            if (_white_id == UINT64_MAX || _black_id == UINT64_MAX) // 存在AI玩家
            {
                json_resp = ai_handle_chess_user(req);
                if (json_resp["winner"].asUInt64() > 0)
                {
                    uint64_t winner_id = json_resp["winner"].asUInt64();
                    Json::Value params, result;
                    params["uid"] = winner_id;
                    if (!_clientp->call("win", params, result))
                    {
                        json_resp["result"] = false;
                        json_resp["reason"] = "rpc调用失败!";
                        ELOG("handle_request: call win failed!");
                    }
                    else if (!result["result"].asBool())
                    {
                        ELOG("handle_request: win err, %s", result["reason"].asCString());
                    }
                    _statu = GAME_OVER;
                }
                else
                {
                    std::string body; // for debug
                    json_util::serialize(json_resp, body);
                    DLOG("room broadcast message: %s", body.c_str());
                    // 轮到AI
                    Json::Value json_resp_tmp = json_resp;
                    broadcast(json_resp);
                    json_resp.clear();
                    json_resp = ai_handle_chess_user_ai();
                    json_resp["room_id"] = req["room_id"];
                    json_resp["uid"] = "18446744073709551615";
                    if (json_resp["winner"].asUInt64() > 0)
                    {
                        uint64_t winner_id = json_resp["winner"].asUInt64();
                        Json::Value params, result;
                        params["uid"] = _black_id;
                        if (!_clientp->call("lose", params, result))
                        {
                            json_resp["result"] = false;
                            json_resp["reason"] = "rpc调用失败!";
                            ELOG("handle_request: call lose failed!");
                        }
                        else if (!result["result"].asBool())
                        {
                            ELOG("handle_request: win err, %s", result["reason"].asCString());
                        }
                        _statu = GAME_OVER;
                    }
                }
            }
            else
            {
                json_resp = handle_chess(req);
                if (json_resp["winner"].asUInt64() > 0)
                {
                    uint64_t winner_id = json_resp["winner"].asUInt64();
                    uint64_t loser_id = winner_id == _white_id ? _black_id : _white_id;
                    if (winner_id != UINT64_MAX)
                    {
                        Json::Value params, result;
                        params["uid"] = winner_id;
                        if (!_clientp->call("win", params, result))
                        {
                            json_resp["result"] = false;
                            json_resp["reason"] = "rpc调用失败!";
                            ELOG("handle_request: call win failed!");
                        }
                        else if (!result["result"].asBool())
                        {
                            ELOG("handle_request: win err, %s", result["reason"].asCString());
                        }
                    }

                    if (loser_id != UINT64_MAX)
                    {
                        Json::Value params, result;
                        params["uid"] = loser_id;
                        if (!_clientp->call("lose", params, result))
                        {
                            json_resp["result"] = false;
                            json_resp["reason"] = "rpc调用失败!";
                            ELOG("handle_request: call lose failed!");
                        }
                        else if (!result["result"].asBool())
                        {
                            ELOG("handle_request: lose err, %s", result["reason"].asCString());
                        }
                    }
                    _statu = GAME_OVER;
                }
            }
        }
        else if (req["optype"].asString() == "chat")
        {
            json_resp = handle_chat(req);
        }
        else if (req["optype"].asString() == "admit_defeat")
        {
            uint64_t curd = req["uid"].asUInt64();
            uint64_t winner_id = curd == _white_id ? _black_id : _white_id;
            uint64_t loser_id = curd == _black_id ? _black_id : _white_id;
            if (winner_id == _white_id)
            {
                json_resp["reason"] = "黑方认输！";
            }
            else
            {
                json_resp["reason"] = "白方认输！";
            }
            json_resp["optype"] = "admit_defeat";
            json_resp["winner"] = (Json::UInt64)winner_id;
            json_resp["result"] = true;
            if (winner_id != UINT64_MAX)
            {
                Json::Value params, result;
                params["uid"] = winner_id;
                if (!_clientp->call("win", params, result))
                {
                    json_resp["result"] = false;
                    json_resp["reason"] = "rpc调用失败!";
                    ELOG("handle_request: call win failed!");
                }
                else if (!result["result"].asBool())
                {
                    ELOG("handle_request: win err, %s", result["reason"].asCString());
                }
                //_tb_user->win(winner_id);
            }

            if (loser_id != UINT64_MAX)
            {
                Json::Value params, result;
                params["uid"] = loser_id;
                if (!_clientp->call("lose", params, result))
                {
                    json_resp["result"] = false;
                    json_resp["reason"] = "rpc调用失败!";
                    ELOG("handle_request: call lose failed!");
                }
                else if (!result["result"].asBool())
                {
                    ELOG("handle_request: lose err, %s", result["reason"].asCString());
                }
            }
            _statu = GAME_OVER;
        }
        else
        {
            json_resp["optype"] = req["optype"].asString();
            json_resp["result"] = false;
            json_resp["reason"] = "未知请求类型";
        }
        // 广播结果
        std::string body; // for debug
        json_util::serialize(json_resp, body);
        DLOG("room broadcast message: %s", body.c_str());
        return broadcast(json_resp);
    }

private:
    bool is_win_wuzi(Json::Value &request, uint64_t &winner_id)
    {
        Json::Value params, result;
        int row = request["row"].asInt();
        int col = request["col"].asInt();
        int color = request["color"].asInt();
        Json::Value board_info;
        for (int i = 0; i < BOARD_ROW; i++)
        {
            for (int j = 0; j < BOARD_COL; j++)
            {
                std::stringstream ss;
                if (_board[i][j] == 0)
                {
                    continue;
                }
                else if (_board[i][j] == 1)
                {
                    ss << i << ',' << j;
                    board_info["white"].append(ss.str());
                }
                else
                {
                    ss << i << ',' << j;
                    board_info["black"].append(ss.str());
                }
            }
        }
        params["board_info"] = board_info;
        params["row"] = row;
        params["col"] = col;
        params["color"] = color;
        params["white_id"] = _white_id;
        params["black_id"] = _black_id;
        if (!_clientp->call("wuzi_win", params, result))
        {
            ELOG("is_win_wuzi: call wuzi_win failed!");
            return false;
        }
        if (!result["result"].asBool())
        {
            ELOG("handle_chess: win err, %s", result["reason"].asCString());
            return false;
        }
        winner_id = 0;
        if (result["winner_id"].asUInt64() > 0)
        {
            winner_id = result["winner_id"].asUInt64();
        }
        return true;
    }

private:
    uint64_t _room_id;
    room_statu _statu;
    int _player_count;
    uint64_t _white_id; // 若为UINT64_MAX表示人机玩家
    uint64_t _black_id;
    online_manager *_online_user;
    std::vector<std::vector<int>> _board;
    bool _is_ai_room;
    Rpc::Client::RpcClient::ptr _clientp;

    int white_captured = 0;
    int black_captured = 0;
};

using room_ptr = std::shared_ptr<room>;
class room_manager
{
public:
    // 初始化房间ID计数器
    room_manager(online_manager *om, Rpc::Client::RpcClient::ptr clientp) : _next_rid(1), _online_user(om), _clientp(clientp)
    {
        ILOG("room_manager init success!");
    }
    ~room_manager()
    {
        ILOG("room_manager destroy!");
    }
    // 为两个用户创建房间，并返回房间的智能指针管理对象
    room_ptr create_room(uint64_t uid1, uint64_t uid2, bool is_ai)
    {
        // 两个用户在游戏大厅中进行对战匹配，匹配成功后才创建房间
        // 校验两个用户是否都还在游戏大厅中，只有都在才创建房间
        if (_online_user->is_in_game_hall(uid1) == false)
        {
            ILOG("用户：%lu 不在大厅中，创建房间失败!", uid1);
            return room_ptr();
        }
        if (_online_user->is_in_game_hall(uid2) == false)
        {
            ILOG("用户：%lu 不在大厅中，创建房间失败!", uid2);
            return room_ptr();
        }
        // 创建房间，将用户id添加到房间中
        std::unique_lock<std::mutex> lock(_mutex);
        room_ptr rp(new room(_next_rid, _online_user, _clientp));
        rp->add_white_user(uid1);
        rp->add_black_user(uid2);
        // 管理房间信息
        _rooms.insert(std::make_pair(_next_rid, rp));
        _users.insert(std::make_pair(uid1, _next_rid));
        _users.insert(std::make_pair(uid2, _next_rid));
        _next_rid++;
        // 返回房间信息
        return rp;
    }
    // 通过房间ID获取房间信息
    room_ptr get_room_by_rid(uint64_t rid)
    {
        std::unique_lock<std::mutex> lock(_mutex);
        auto ret = _rooms.find(rid);
        if (ret == _rooms.end())
        {
            ELOG("room_manager: rid->room_ptr empty!");
            return room_ptr();
        }
        return ret->second;
    }
    // 通过用户ID获取房间信息
    room_ptr get_room_by_uid(uint64_t uid)
    {
        std::unique_lock<std::mutex> lock(_mutex);
        // 通过用户ID获取房间ID
        auto uit = _users.find(uid);
        if (uit == _users.end())
        {
            ELOG("room_manager: uid->rid empty!");
            return room_ptr();
        }
        uint64_t rid = uit->second;
        auto ret = _rooms.find(rid);
        if (ret == _rooms.end())
        {
            ELOG("room_manager: rid->roomptr empty!");
            return room_ptr();
        }
        return ret->second;
    }
    // 通过房间ID销毁房间
    void remove_room(uint64_t rid)
    {
        // 将shared_ptr从_rooms中移除,外界没有对房间信息进行操作保存的情况下就会释放
        // 通过房间ID，获取房间信息
        room_ptr rp = get_room_by_rid(rid);
        if (rp.get() == nullptr)
        {
            return;
        }
        // 移除房间管理中的用户信息
        uint64_t wuid = rp->get_white_user();
        uint64_t buid = rp->get_black_user();
        std::unique_lock<std::mutex> lock(_mutex);
        _users.erase(wuid);
        _users.erase(buid);
        // 移除房间管理信息
        _rooms.erase(rid);
    }
    // 删除房间中指定用户，如果房间中没有用户了，则销毁房间，用户连接断开时被调用
    void remove_room_user(uint64_t uid)
    {
        auto rp = get_room_by_uid(uid);
        if (rp.get() == nullptr)
        {
            return;
        }
        rp->handle_exit(uid);
        if (rp->get_white_id() == UINT64_MAX)
        {
            remove_room(rp->id());
        }
        else if (rp->player_count() == 0)
        {
            remove_room(rp->id());
        }
        return;
    }

private:
    uint64_t _next_rid; // 下一个房间id
    std::mutex _mutex;
    online_manager *_online_user;
    std::unordered_map<uint64_t, room_ptr> _rooms; // 房间id关联的房间
    std::unordered_map<uint64_t, uint64_t> _users; // 用户uid关联的房间

    Rpc::Client::RpcClient::ptr _clientp;
};